﻿using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using FFmpeg.AutoGen;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.Lib.AdvancedFragmentProcessor;
using FFmpegVideoClip.Mode;
using FFmpegVideoClip.windows;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;

namespace FFmpegVideoClip.Lib
{
    public static class Tools
    {
        public static readonly Regex GetJsonRegex = new Regex(@"(\{(?:[^{}]|(?<o>\{)|(?<-o>\}))+(?(o)(?!))\}|$(?:[^\[$]|(?<o>$)|(?<-o>$))+(?(o)(?!))\])", RegexOptions.Compiled);
        private readonly static char[] RandomCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".ToCharArray();
        public const string TimeSpan_hhmmssfff = @"hh\:mm\:ss\.fff";
        public const string TimeZero = "00:00:00.000";
        public const string TimeShortZero = "00:00:00";
        public const string TimeUltraShortZero = "0:0:0";
        public static readonly Regex InvalidFileNameRegex = new Regex($"[{Regex.Escape(new string(Path.GetInvalidFileNameChars()))}]", RegexOptions.Compiled);
        public static readonly Regex InvalidPathRegex = new Regex($"[{Regex.Escape(new string(Path.GetInvalidPathChars()))}]", RegexOptions.Compiled);
        private static CancellationTokenSource? m_CurrentCancelToken;
        static ReaderWriterLockSlim CurrentCancelTokenLock = new();
        public static CancellationTokenSource? CurrentCancelToken
        {
            get
            {
                CurrentCancelTokenLock.EnterReadLock();
                CancellationTokenSource? result = m_CurrentCancelToken;
                CurrentCancelTokenLock.ExitReadLock();
                return result;
            }
            set
            {
                CurrentCancelTokenLock.EnterUpgradeableReadLock();
                try
                {
                    if (m_CurrentCancelToken == value)
                        return;
                    CurrentCancelTokenLock.EnterWriteLock();
                    m_CurrentCancelToken = value;
                    CurrentCancelTokenLock.ExitWriteLock();
                }
                finally { CurrentCancelTokenLock.ExitUpgradeableReadLock(); }
            }
        }
        public static bool IsCancellationRequested => CurrentCancelToken?.IsCancellationRequested ?? true;

        [DllImport("User32")] public extern static void SetCursorPos(int x, int y);
        public static void SetCursorPos(double x, double y) => SetCursorPos((int)Math.Round(x, MidpointRounding.AwayFromZero), (int)Math.Round(y, MidpointRounding.AwayFromZero));
        public static TimeSpan Sum(this IEnumerable<TimeSpan> Source)
        {
            TimeSpan ts = new TimeSpan();
            foreach (TimeSpan t in Source)
                ts = ts.Add(t);
            return ts;
        }
        public static string GetTimeSpanToString(TimeSpan time)
            => $"{Math.Floor(time.TotalHours).ToString("0000")}:{time.Minutes.ToString("00")}:{time.Seconds.ToString("00")}.{time.Milliseconds.ToString("000")}";
        public static string GetTimeSpanToString_HoursMinutesSeconds(TimeSpan time)
            => $"{Math.Floor(time.TotalHours)}小时{time.Minutes.ToString("00")}分钟{time.Seconds.ToString("00")}秒";
        private static int GetTimeRoundSecond(TimeSpan time) => time.Seconds + (time.Milliseconds > 500 ? 1 : 0);
        public static string GetTimeSpanToString_HoursMinutesSecondsDigit(TimeSpan time) => $"{Math.Floor(time.TotalHours)}:{time.Minutes.ToString("00")}:{time.Seconds.ToString("00")}.{time.Milliseconds}";
        public static string GetTimeSpanToString_Digit(TimeSpan time, bool EnableAutoHours = true, bool EnableMilliseconds = true)
        {
            if (EnableAutoHours && time.TotalHours > 1)
            {
                if (EnableMilliseconds)
                    return $"{Math.Floor(time.TotalHours)}:{time.Minutes.ToString("00")}:{time.Seconds.ToString("00")}.{time.Milliseconds}";
                else
                    return $"{Math.Floor(time.TotalHours)}:{time.Minutes.ToString("00")}:{GetTimeRoundSecond(time).ToString("00")}";
            }
            else
            {
                if (EnableMilliseconds)
                    return $"{Math.Floor(time.TotalMinutes)}:{time.Seconds.ToString("00")}.{time.Milliseconds}";
                else
                    return $"{Math.Floor(time.TotalMinutes)}:{GetTimeRoundSecond(time).ToString("00")}";
            }
        }
        public static string GetTimeSpanToString_RoundSeconds(TimeSpan time) => $"{Math.Floor(time.TotalHours).ToString("000")}:{time.Minutes.ToString("00")}:{GetTimeRoundSecond(time).ToString("00")}";
        public static string FormatTimeSpanToMediaTime(TimeSpan value) => $"{Math.Floor(value.TotalHours).ToString("00")}:{value.Minutes.ToString("00")}:{value.Seconds.ToString("00")}.{value.Milliseconds}";
        public static string GetStringTimeTrimZero(TimeSpan time)
        {
            if (time <= TimeSpan.Zero)
                return "0";
            Span<char> result = $"{time.Days * 24 + time.Hours}:{time.Minutes}:{time.Seconds}.{time.Milliseconds}".TrimStart(':').TrimStart('.').ToCharArray();
            while (result[0] == '0' || result[0] == ':' || result[0] == '.')
                result = result[1..];
            return new string(result);
        }
        public static string GetStringTimeTrimZero(string time) => GetStringTimeTrimZero(GetStringTimeToTimeSpan(time));
        public static TimeSpan GetStringTimeToTimeSpan(string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new FormatException($"字符串：[{value}],不符合时间格式");

            string[] SA = value.Split([':', '.', '-'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            Span<int> ints = stackalloc int[SA.Length];

            for (int i = 0; i < ints.Length; i++)
            {
                if (!int.TryParse(SA[i], out int Number))
                    throw new FormatException($"字符串：[{value}],不符合时间格式");
                ints[i] = Number;
            }

            return ints.Length switch
            {
                1 => new TimeSpan(0, 0, 0, 0, ints[0]),
                2 => new TimeSpan(0, 0, 0, ints[0], ints[1]),
                3 => new TimeSpan(0, 0, ints[0], ints[1], ints[2]),
                4 => new TimeSpan(0, ints[0], ints[1], ints[2], ints[3]),
                5 => new TimeSpan(ints[0], ints[1], ints[2], ints[3], ints[4]),
                _ => throw new FormatException($"字符串：[{value}],不符合时间格式,或者字符串属于尚未实现的格式")
            };
        }
        public static TimeSpan GetStringTimeToTimeSpanFormSystemStandards(string time)
        {
            TimeSpan result;
            if (TimeSpan.TryParseExact(time, "c", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, "g", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, "G", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"s", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"s\.f", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"s\.ff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"s\.fff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"s\.ffff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"s\.fffff", null, out result))
                return result;

            if (TimeSpan.TryParseExact(time, @"m\:s", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"m\:s\.f", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"m\:s\.ff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"m\:s\.fff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"m\:s\.ffff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"m\:s\.fffff", null, out result))
                return result;

            if (TimeSpan.TryParseExact(time, @"h\:m\:s", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"h\:m\:s\.f", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"h\:m\:s\.ff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"h\:m\:s\.fff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"h\:m\:s\.ffff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"h\:m\:s\.fffff", null, out result))
                return result;

            if (TimeSpan.TryParseExact(time, @"d\.h\:m\:s", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\.h\:m\:s\.f", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\.h\:m\:s\.ff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\.h\:m\:s\.fff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\.h\:m\:s\.ffff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\.h\:m\:s\.fffff", null, out result))
                return result;

            if (TimeSpan.TryParseExact(time, @"d\:h\:m\:s", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\:h\:m\:s\.f", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\:h\:m\:s\.ff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\:h\:m\:s\.fff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\:h\:m\:s\.ffff", null, out result))
                return result;
            if (TimeSpan.TryParseExact(time, @"d\:h\:m\:s\.fffff", null, out result))
                return result;
            throw new InvalidCastException($"{nameof(time)}:[{time}] 格式不支持");
        }
        public static bool CheckStartAndEndTime()
        {
            TimeSpan Start = GetStringTimeToTimeSpan(ClipManage.CurrentEditFragment.StartTimeValue);
            TimeSpan End = GetStringTimeToTimeSpan(ClipManage.CurrentEditFragment.EndTimeValue);
            if (End < Start)
                return false;
            return true;
        }

        /// <summary>
        /// 检查片段名称
        /// </summary>
        /// <param name="window"></param>
        /// <param name="clip">空参时不检查重名</param>
        /// <param name="fragment"></param>
        /// <returns></returns>
        public static bool CheckFragmentName(Window window, Clip? clip, Fragment fragment)
        {
            if (string.IsNullOrEmpty(fragment.Name))
            {
                MessageBoxEx.Show(window, "当前片段未命名", "片段名称错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }

            if (clip is null)
                return true;



            foreach (Fragment item in clip.Fragments.Where(p => p.ID != fragment.ID))
            {
                if (item.Name.Equals(fragment.Name, StringComparison.OrdinalIgnoreCase))
                {
                    MessageBoxEx.Show(window, "当前片段与目标剪辑重名", "片段名称错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 不使用重命名时，无重名(通过)，返回true,当参与元素少于2个(不包含2个)始终返回true
        /// </summary>
        /// <param name="Group1"></param>
        /// <param name="Group2"></param>
        /// <param name="EnableReName"></param>
        /// <returns></returns>
        public static bool CheckFragmentName(IEnumerable<Fragment> Group1, IEnumerable<Fragment> Group2, bool EnableReName = false)
        {
            IEnumerable<Fragment> all = Group2.Union(Group1);
            if (all.Count() < 2)
                return true;
            if (!EnableReName)
                return all.GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase).Where(p => p.Count() > 1).Count() < 1;
            Queue<Fragment> queue = new(all);
            Fragment Start = queue.Peek();
            int i = 0;
            while (all.GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase).Where(p => p.Count() > 1).Count() > 0 && i++ < 4)
            {
                //首个
                Fragment current = queue.Dequeue();
                while (queue.Any(p => p.Name.Equals(current.Name, StringComparison.OrdinalIgnoreCase)))
                    current.Name += "(重命名)";
                queue.Enqueue(current);
                current = queue.Dequeue();
                //后续
                while (current != Start)
                {
                    while (queue.Any(p => p.Name.Equals(current.Name, StringComparison.OrdinalIgnoreCase)))
                        current.Name += "(重命名)";
                    queue.Enqueue(current);
                    current = queue.Dequeue();
                }
            }
            return all.GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase).Where(p => p.Count() > 1).Count() < 1;
        }

        public static bool InputToIndex(string Input, out int InputIndex)
        {
            if (string.IsNullOrEmpty(Input) || Input.Equals(ParametersSign.EmptyParameter, StringComparison.OrdinalIgnoreCase))
            {
                InputIndex = -1;
                return true;
            }
            return int.TryParse(Input, out InputIndex);
        }

        public static bool CheckFlowsParameter(IEnumerable<AdvancedParametersSchemeModule> modules, out string Info)
        {
            StringBuilder sb = new StringBuilder();
            string temp;
            foreach (AdvancedParametersSchemeModule module in modules)
            {
                foreach (ParametersSign ps in module.ParametersSignList.Where(p =>
                !(p.SignName.Equals(ParametersSign.STime, StringComparison.OrdinalIgnoreCase)
                 || p.SignName.Equals(ParametersSign.ETime, StringComparison.OrdinalIgnoreCase)
                 || p.SignName.StartsWith(ParametersSign.InputSignNamePattern, StringComparison.OrdinalIgnoreCase))))
                {
                    if (!(temp = ps.Parameter.Trim()).Equals(ParametersSign.EmptyParameter) && string.IsNullOrWhiteSpace(temp))
                    {
                        sb.AppendLine($"[{module.SchemeName}]->[{ps.ShowName}]:未赋值");
                    }
                }
            }
            if (sb.Length > 1)
            {
                //MessageBoxEx.Show(Application.Current.MainWindow,sb.ToString(),"未赋值的参数",MessageBoxButton.OK, MessageBoxImage.Error);
                Info = sb.ToString();
                return false;
            }
            Info = null!;
            return true;
        }

        public static OpenFileDialog GetOpenFileDialog(string InitialDirectory, string Title, string DefaultExt, string Filter)
        {
            OpenFileDialog ofd = new();
            if (!string.IsNullOrEmpty(InitialDirectory) && Directory.Exists(InitialDirectory))
                ofd.InitialDirectory = InitialDirectory;
            if (string.IsNullOrEmpty(ofd.InitialDirectory) || !Directory.Exists(ofd.InitialDirectory))
            {
                string mp = ((MainWindow)Application.Current.MainWindow).Mec.CurrentMediaPath;
                ofd.InitialDirectory = !string.IsNullOrEmpty(MediaElementConcrol.LastPath) && Directory.Exists(MediaElementConcrol.LastPath) ? MediaElementConcrol.LastPath
                : !string.IsNullOrEmpty(mp) && Directory.Exists(Path.GetDirectoryName(mp)) ? Path.GetDirectoryName(mp)
                : !string.IsNullOrEmpty(ClipManage.OldSavePath) && Directory.Exists(ClipManage.OldSavePath) ? ClipManage.OldSavePath
                : Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
            }
            ofd.CheckFileExists = true;
            ofd.Title = Title;
            ofd.DefaultExt = DefaultExt;
            ofd.Filter = Filter;
            return ofd;
        }
        public static bool OpenFolderDialog(string InitialDirectory, string Title, out string ResultPath)
        {
            ResultPath = string.Empty;
            using (CommonOpenFileDialog dialog = new CommonOpenFileDialog())
            {
                if (!string.IsNullOrEmpty(InitialDirectory) && Directory.Exists(InitialDirectory))
                    dialog.InitialDirectory = InitialDirectory;
                if (string.IsNullOrEmpty(dialog.InitialDirectory) || !Directory.Exists(dialog.InitialDirectory))
                {
                    string mp = ((MainWindow)Application.Current.MainWindow).Mec.CurrentMediaPath;
                    dialog.InitialDirectory = !string.IsNullOrEmpty(MediaElementConcrol.LastPath) && Directory.Exists(MediaElementConcrol.LastPath) ? MediaElementConcrol.LastPath
                    : !string.IsNullOrEmpty(mp) && Directory.Exists(Path.GetDirectoryName(mp)) ? Path.GetDirectoryName(mp)
                    : !string.IsNullOrEmpty(ClipManage.OldSavePath) && Directory.Exists(ClipManage.OldSavePath) ? ClipManage.OldSavePath
                    : Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
                }
                dialog.Title = Title;
                dialog.IsFolderPicker = true;
                dialog.EnsurePathExists = true;
                if (dialog.ShowDialog() == CommonFileDialogResult.Cancel)
                    return false;
                ResultPath = dialog.FileName;
            }
            return true;
        }
        public static void Explorer(string Path, bool EnableSelect = false)
        {
            if (EnableSelect)
                System.Diagnostics.Process.Start("Explorer", $"/select, \"{Path}\"");
            else
                System.Diagnostics.Process.Start("Explorer", $"/e, \"{Path}\"");
        }

        public static void CloseKeyInputEditor(UIElement current)
        {
            DependencyObject DoUI = current;
            while (!(DoUI is null || DoUI is Window || DoUI is Page))
                DoUI = VisualTreeHelper.GetParent(DoUI);

            try
            {
                if (DoUI is null || DoUI is not UIElement)
                    throw new InvalidDataException($"[{nameof(CloseKeyInputEditor)}]失败：未查询到元素[Window或Page]或当前查询结果无法转换到[UIElement]类型");

                DoUI = VisualTreeHelper.GetChild(DoUI, 0);

                if (DoUI is null || DoUI is not UIElement)
                    throw new InvalidDataException($"[{nameof(CloseKeyInputEditor)}]失败：未查询到[Window或Page]的根元素或当前查询结果无法转换到[UIElement]类型");
            }
            catch (Exception exc)
            {
                LogManage.WriteExceptionInfo("关闭键盘编辑状态失败", exc, true);
                return;
            }

            current = (UIElement)DoUI;
            if (current.Focusable)
            {
                Keyboard.Focus(current);
                return;
            }
            current.Focusable = true;
            Keyboard.Focus(current);
            current.Focusable = false;
        }

        public static void CheckFragmentID()
        {
            IEnumerable<Fragment> all = ClipManage.ClipList.SelectMany(x => x.Fragments);
            if (!all.Any())
                return;
            IEnumerable<Fragment> fs = ClipManage.ClipList.SelectMany(x => x.Fragments).Where(p => p.ID < 0).Union(GetRepeatID(all));
            if (!fs.Any())
                return;
            int current = all.Max(x => x.ID) + 1;
            foreach (Fragment fragment in fs)
                fragment.ID = current++;
        }
        private static IEnumerable<Fragment> GetRepeatID(IEnumerable<Fragment> Elements)
        {
            Set<int> set = new Set<int>();
            foreach (Fragment fragment in Elements)
            {
                if (!set.Add(fragment.ID))
                    yield return fragment;
            }
        }
        public static void SplitFragment(Fragment fragment, Action<string>? OnError = null)
        {
            SplitFragmentWindow window = new(fragment);
            if (window.ShowDialog() is false)
                return;
            switch (window.resultData.SplitModeValue)
            {
                case SplitFragmentWindow.SplitModeEnum.FixedSize:
                    FixedSplit(fragment, GetStringTimeToTimeSpan(window.resultData.Value), OnError);
                    break;
                case SplitFragmentWindow.SplitModeEnum.AverageTime:
                    AverageSplit(fragment, int.Parse(window.resultData.Value), OnError);
                    break;
                case SplitFragmentWindow.SplitModeEnum.FixedKeyFrame:
                case SplitFragmentWindow.SplitModeEnum.FixedFrame:
                case SplitFragmentWindow.SplitModeEnum.AverageKeyFrames:
                case SplitFragmentWindow.SplitModeEnum.AverageFrames:
                    TimePointSplitFragment(fragment, window.resultData.TimeSpans, OnError: OnError);
                    break;
                case SplitFragmentWindow.SplitModeEnum.UserTime:
                    SpecifyTimeSplitCut(window.resultData);
                    break;
                default:
                    return;
            }
            fragment.clip?.VerifyFragmentsDuplicateName(true);
        }
        public static readonly TimeSpan OneMillisecond = TimeSpan.FromMilliseconds(1);
        public static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
        private static string? GetFFprobeRead_intervals(TimeSpan[][]? TimeRange)
        {
            string? Ranges = null;
            StringBuilder sb = new StringBuilder();
            if (TimeRange != null && TimeRange.Length > 0)
            {
                foreach (TimeSpan[] Range in TimeRange)
                {
                    sb.Append($"{Range[0].TotalSeconds}%{Range[1].TotalSeconds},");
                }
                Ranges = sb.ToString().TrimEnd(',');
            }
            return Ranges;
        }
        public static TimeSpan[] GetFixedTime(TimeSpan Start, TimeSpan End, TimeSpan span)
        {
            if (End <= Start || End - Start < span)
            {
                LogManage.WriteLineNewInfo($"参数错误:开始:{Start} 结束{End} 间隔{span}");
                return [];
            }
            TimeSpan CurrentTime = Start + span;
            List<TimeSpan> TimeSpans = [CurrentTime];
            while (CurrentTime < End)
                TimeSpans.Add(CurrentTime += span);
            return TimeSpans.Where(p => p < End).ToArray();
        }
        private static void FixedSplit(Fragment fragment, TimeSpan span, Action<string>? OnError = null)
        {
            if (fragment == null)
                throw new ArgumentNullException(nameof(fragment));
            if (fragment.clip == null)
                throw new NullReferenceException(nameof(fragment.clip));

            TimePointSplitFragment(fragment, GetFixedTime(GetStringTimeToTimeSpan(fragment.StartTimeValue), GetStringTimeToTimeSpan(fragment.EndTimeValue), span), OnError: OnError);
        }
        private static void AverageSplit(Fragment fragment, int SplitCount, Action<string>? OnError = null)
        {
            if (fragment == null)
                throw new ArgumentNullException(nameof(fragment));
            if (fragment.clip == null)
                throw new NullReferenceException(nameof(fragment.clip));
            if (SplitCount < 2)
            {
                OnError?.Invoke("平均分割指定的片段数不能小于2");
                return;
            }

            TimePointSplitFragment(fragment, GetTimeAverageSplit(GetStringTimeToTimeSpan(fragment.StartTimeValue), GetStringTimeToTimeSpan(fragment.EndTimeValue), SplitCount), OnError: OnError);

        }
        private static TimeSpan[] GetTimeAverageSplit(TimeSpan StartTime, TimeSpan EndTIme, int SplitCount)
        {
            if (SplitCount < 2 && EndTIme <= StartTime)
                return [];
            TimeSpan TotalDuration = EndTIme - StartTime;
            TimeSpan span = TotalDuration / SplitCount;
            TimeSpan[] Result = new TimeSpan[SplitCount - 1];

            for (int i = 1; i < SplitCount; i++)
                Result[i - 1] = StartTime + span * i;
            return Result;
        }
        private static TimeSpan[][] TimePointToTimeRange(IEnumerable<TimeSpan> TimePoint, double RangeDurationSeconds = 1.5, TimeSpan? MinTime = null, TimeSpan? MaxTime = null)
        {
            List<TimeSpan[]> timeSpans = new List<TimeSpan[]>();
            TimeSpan HalfTime = TimeSpan.FromSeconds(RangeDurationSeconds) / 2;
            TimeSpan sTime, eTime;
            foreach (TimeSpan timePoint in TimePoint)
            {
                sTime = timePoint - HalfTime;
                eTime = timePoint + HalfTime;

                if (MinTime.HasValue && sTime < MinTime)
                    sTime = MinTime.Value;
                if (MaxTime.HasValue && eTime > MaxTime)
                    eTime = MaxTime.Value;

                timeSpans.Add([sTime, eTime]);
            }
            return timeSpans.ToArray();
        }
        public static TimeSpan[] GetFramesTimes(string FilePath, TimeSpan[][] TimeRange, bool IsKeyFrameMode = false, Action<TimeSpan>? CurrentTimeSpan = null, CancellationTokenSource? cancellationTokenSource = null)
        {
            string? Ranges = GetFFprobeRead_intervals(TimeRange);
            string? line = string.Empty;
            List<TimeSpan> KeyframesTimeList = new();
            using (Process ffprobe = new())
            {
                TimeSpan current;
                ffprobe.StartInfo.FileName = "ffprobe";
                ffprobe.StartInfo.Arguments = $"-loglevel error -i \"{FilePath}\" {(IsKeyFrameMode ? "-skip_frame nokey" : "")} -select_streams v:0 -show_entries frame=pts_time -of csv=print_section=0 {(Ranges != null ? $"-read_intervals {Ranges}" : string.Empty)}";
                ffprobe.StartInfo.CreateNoWindow = true;
                ffprobe.StartInfo.UseShellExecute = false;
                ffprobe.StartInfo.RedirectStandardOutput = true;
                ffprobe.StartInfo.RedirectStandardError = true;
                ffprobe.ErrorDataReceived += (s, e) => { LogManage.WriteNewInfo(e.Data ?? string.Empty); };
                ffprobe.Start();
                ffprobe.BeginErrorReadLine();
                LogManage.WriteLineNewInfo($"获取帧信息:[{ffprobe.StartInfo.FileName} {ffprobe.StartInfo.Arguments}]");
                while (line != null)
                {
                    if (cancellationTokenSource != null && cancellationTokenSource.IsCancellationRequested)
                    {
                        ffprobe.Kill(true);
                        return [];
                    }
                    line = ffprobe.StandardOutput.ReadLine()?.TrimEnd(',');
                    if (string.IsNullOrWhiteSpace(line) || !double.TryParse(line, out double value))
                        continue;
                    current = TimeSpan.FromSeconds(value);
                    KeyframesTimeList.Add(current);
                    CurrentTimeSpan?.Invoke(current);
                }
            }
            return KeyframesTimeList.ToArray();
        }
        public static TimeSpan[] GetFramesTimes(string FilePath, TimeSpan StartTime, TimeSpan EndTIme, int SplitCount, bool IsKeyFrameMode = false, Action<TimeSpan>? CurrentTimeSpan = null, CancellationTokenSource? cancellationTokenSource = null)
        {
            TimeSpan[] times = GetTimeAverageSplit(StartTime, EndTIme, SplitCount);
            TimeSpan[][] Range = TimePointToTimeRange(times, MinTime: StartTime, MaxTime: EndTIme);
            TimeSpan[] FramesTimes = GetFramesTimes(FilePath, Range, IsKeyFrameMode, CurrentTimeSpan, cancellationTokenSource);

            for (int i = 0; i < times.Length; i++)
                times[i] = GetClosestTime(times[i], FramesTimes);

            if (cancellationTokenSource != null && cancellationTokenSource.IsCancellationRequested)
                return [];

            return times;
        }
        public static TimeSpan[] GetFramesTimes(string FilePath, TimeSpan SplitPoint, bool IsKeyFrameMode = false, double RangeDurationSeconds = 1.5, TimeSpan? MinTime = null, TimeSpan? MaxTime = null, Action<TimeSpan>? CurrentTimeSpan = null, CancellationTokenSource? cancellationTokenSource = null)
        {
            TimeSpan[][] Range = TimePointToTimeRange([SplitPoint], RangeDurationSeconds, MinTime, MaxTime);
            TimeSpan[] FramesTimes = GetFramesTimes(FilePath, Range, IsKeyFrameMode, CurrentTimeSpan, cancellationTokenSource);

            if (FramesTimes.Length < 1)
                return [];
            else if (FramesTimes.Length < 2)
                return [FramesTimes[0]];
            else
                return [GetClosestTime(SplitPoint, FramesTimes)];
        }
        public static TimeSpan[] GetFramesTimes(string FilePath, TimeSpan[] SplitPoints, bool IsKeyFrameMode = false, double RangeDurationSeconds = 1.5, TimeSpan? MinTime = null, TimeSpan? MaxTime = null, Action<TimeSpan>? CurrentTimeSpan = null, CancellationTokenSource? cancellationTokenSource = null)
        {
            TimeSpan[][] Range = TimePointToTimeRange(SplitPoints, RangeDurationSeconds, MinTime, MaxTime);
            TimeSpan[] FramesTimes = GetFramesTimes(FilePath, Range, IsKeyFrameMode, CurrentTimeSpan, cancellationTokenSource);

            for (int i = 0; i < SplitPoints.Length; i++)
                SplitPoints[i] = GetClosestTime(SplitPoints[i], FramesTimes);

            if (cancellationTokenSource != null && cancellationTokenSource.IsCancellationRequested)
                return [];
            return SplitPoints;
        }
        private static Fragment[] TimePointSplitFragment(Fragment fragment, TimeSpan[]? TimeData, bool EnableCheckClip = false, Action<string>? OnError = null)
        {
            if (EnableCheckClip && fragment.clip == null)
            {
                MainWindow.ShowStatusInfo("片段没有关联的剪辑实例，不支持该操作");
                return [];
            }
            if (TimeData == null || TimeData.LongLength < 1)
            {
                LogManage.WriteLineNewInfo($"关键帧[{nameof(TimeData)}]数量少于分割数量[{1}]个");
                return [];
            }

            Fragment CurrentFragment;
            Clip? clip = fragment.clip;
            List<Fragment> result = new List<Fragment>();
            TimeSpan startTime = GetStringTimeToTimeSpan(fragment.StartTimeValue);
            TimeSpan endTIme = GetStringTimeToTimeSpan(fragment.EndTimeValue);

            SplitTools.SplitTimeRange[] SplitFrameTime = SplitTools.SplitTimes(TimeData, startTime, endTIme, Config.SplitMode);

            if (SplitFrameTime.Length < 2)
            {
                MainWindow.ShowStatusInfo("计算时间少于2个，分割失败");
                return [];
            }

            int FrameIndex = 0;
            string GetNewName() => $"{fragment.Name}_[SplitFragment{FrameIndex}]";

            if (clip != null)
            {
                FrameIndex = clip.IndexOf(fragment);
                clip.Remove(fragment);
            }

            foreach (SplitTools.SplitTimeRange timeRange in SplitFrameTime)
            {
                CurrentFragment = new();
                fragment.CopyTo(CurrentFragment);
                CurrentFragment.StartTimeValue = GetTimeSpanToString(timeRange.StartTime);
                CurrentFragment.EndTimeValue = GetTimeSpanToString(timeRange.EndTime);
                result.Add(CurrentFragment);
                if (clip != null)
                {
                    CurrentFragment.ID = ClipManage.NewFragmentID();
                    CurrentFragment.Name = GetNewName();
                    clip.Insert(FrameIndex++, CurrentFragment);
                }
            }
            ClipManage.NormalizationFragmentsID();
            return result.ToArray();
        }
        public static void SpecifyTimeSplitCut(SplitFragmentWindow.ResultData resultData)
        {
            TimeSpan SplitTime = GetStringTimeToTimeSpan(resultData.Value);
            SplitTools.SplitTimePosition timePosition;
            if (resultData.FramesMode == SplitFragmentWindow.KeyFrameModeEnum.None || resultData.TimeSpans == null || resultData.TimeSpans.Length < 1)
                timePosition = SplitTools.SplitTime(SplitTime, Config.SplitMode);
            else
                timePosition = SplitTools.SplitTime(GetClosestTime(SplitTime, resultData.TimeSpans), Config.SplitMode);
            Clip CurrentClip;
            if (ClipManage.CurrentEditFragment.clip == null)
            {
                if (!ClipManage.CreateNewClip(out CurrentClip))
                    return;
            }
            else CurrentClip = ClipManage.CurrentEditFragment.clip;
            Fragment CurrentFragment, NextFragment;
            if (!CurrentClip.GetFragment(ClipManage.CurrentEditFragment.ID, out CurrentFragment))
            {
                CurrentFragment = new();
                ClipManage.CurrentEditFragment.CopyTo(CurrentFragment);
                CurrentFragment.ID = ClipManage.NewFragmentID();
                CurrentFragment.Name = $"Fragment_Split_{CurrentClip.Fragments.Count + 1}";
                CurrentClip.AddFragment(CurrentFragment);
            }
            NextFragment = new();
            CurrentFragment.CopyTo(NextFragment);
            NextFragment.ID = ClipManage.NewFragmentID();

            CurrentFragment.EndTimeValue = GetTimeSpanToString(timePosition.BeforeTime);
            NextFragment.StartTimeValue = GetTimeSpanToString(timePosition.AfterTime);
            int index = CurrentClip.IndexOf(CurrentFragment) + 1;
            NextFragment.Name = $"Fragment_Split_{index}";
            CurrentClip.Insert(index, NextFragment);
            ClipManage.NormalizationFragmentsID();
        }
        private static TimeSpan GetClosestTime(TimeSpan SpecifyTime, IEnumerable<TimeSpan> Times)
        {
            if (Times == null || !Times.Any())
            {
                throw new ArgumentOutOfRangeException($"错误:{nameof(Times)}元素数量:0个");
            }

            TimeSpan MinClosestTime = Times.First();
            if (Times.Count() == 1)
                return MinClosestTime;

            long MinDifference = Math.Abs(SpecifyTime.Ticks - MinClosestTime.Ticks);
            long CurrentDifference;
            foreach (TimeSpan CurrentTime in Times.Skip(1))
            {
                CurrentDifference = Math.Abs(SpecifyTime.Ticks - CurrentTime.Ticks);
                if (CurrentDifference < MinDifference)
                {
                    MinDifference = CurrentDifference;
                    MinClosestTime = CurrentTime;
                }
            }
            return MinClosestTime;
        }
        public static Window GetVisualElementParentWindow(DependencyObject dependencyObject) => Window.GetWindow(dependencyObject);
        public static bool GetVisualElementOfParent<T>(DependencyObject dependencyObject, out T? Result) where T : DependencyObject
        {
            Result = null;
            while (dependencyObject != null)
            {
                if (dependencyObject is T TValue)
                {
                    Result = TValue;
                    return true;
                }
                dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
            }
            return false;
        }
        public static bool GetVisualElementInChilds<T>(DependencyObject dependencyObject, out T? Result) where T : DependencyObject
        {
            Result = null;
            HashSet<DependencyObject> Cache = new HashSet<DependencyObject>();
            Cache.Add(dependencyObject);
            while (Cache.Count > 0)
            {
                dependencyObject = Cache.First();
                Cache.Remove(dependencyObject);
                if (dependencyObject is T TValue)
                {
                    Result = TValue;
                    return true;
                }
                GetVisualElementInChilds(dependencyObject, Cache);
            }
            return false;
        }
        public static bool GetVisualElementInChilds(DependencyObject dependencyObject, HashSet<DependencyObject> Result)
        {
            if (Result == null)
                Result = new HashSet<DependencyObject>();
            if (dependencyObject == null)
                return false;
            int count = VisualTreeHelper.GetChildrenCount(dependencyObject);
            if (count < 1)
                return false;
            for (int i = 0; i < count; i++)
                Result.Add(VisualTreeHelper.GetChild(dependencyObject, i));
            return true;
        }

        public static void TrimInvalidFileNameChars(ref string value)
        {
            if (InvalidFileNameRegex.IsMatch(value))
                value = InvalidFileNameRegex.Replace(value, string.Empty);
        }
        public static void TrimInvalidPathChars(ref string value)
        {
            if (InvalidPathRegex.IsMatch(value))
                value = InvalidPathRegex.Replace(value, string.Empty);
        }

        public static string CheckFullFileName(string fileName)
        {
            string Dir = Path.GetDirectoryName(fileName)!;
            string ex = Path.GetExtension(fileName)!;
            string Name = Path.GetFileNameWithoutExtension(fileName);
            while (File.Exists(fileName))
                fileName = Path.Combine(Dir, $"{Name}(重命名){ex}");
            return fileName;
        }
        public static void FileMoveTo(Window? owner, params Fragment[] Fragments)
        {
            if (Fragments == null || Fragments.Length < 1)
                return;
            string? m_Path = null;
            IEnumerable<IGrouping<string, Fragment>> fragmentGroups = Fragments.GroupBy(f => f.InputPath);
            foreach (string InputPath in fragmentGroups.Select(p => p.Key))
            {
                if (!File.Exists(InputPath))
                    continue;
                m_Path = Path.GetDirectoryName(InputPath);
                if (Directory.Exists(m_Path))
                    break;
            }
            if (!Directory.Exists(m_Path))
            {
                MessageBoxEx.Show(owner, "源路径无效，不存在有效源文件");
                return;
            }
            if (OpenFolderDialog(m_Path, "指定[移动到]的目录", out string ResultPath))
            {
                if (!Directory.Exists(ResultPath))
                {
                    if (MessageBoxEx.Show(owner, "指定路径不存在，是否创建路径", "路径不存在", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.Cancel)
                        return;
                    Directory.CreateDirectory(ResultPath);
                }
                string NewFilePath;
                StringBuilder sb = new StringBuilder();
                foreach (IGrouping<string, Fragment> FragmentGroup in fragmentGroups)
                {
                    if (!File.Exists(FragmentGroup.Key))
                        continue;
                    NewFilePath = CheckFullFileName(Path.Combine(ResultPath, Path.GetFileName(FragmentGroup.Key)));
                    try
                    {
                        File.Move(FragmentGroup.Key, NewFilePath);
                    }
                    catch (Exception ex)
                    {
                        LogManage.WriteExceptionInfo("移动文件失败:", ex, true);
                        sb.AppendLine(Path.GetFileName(FragmentGroup.Key));
                        continue;
                    }
                    foreach (Fragment fragment in FragmentGroup)
                        fragment.InputPath = NewFilePath;
                }
                if (sb.Length > 1)
                    MessageBoxEx.Show(owner, $"移动失败的文件:\r\n{sb.ToString()}");
            }
        }

        public static string MergeSchemesNamesToUIText(IEnumerable<string> SchemeNames)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string SchemeName in SchemeNames)
                sb.AppendLine($"[{SchemeName.Trim()}]");
            return sb.ToString();
        }

        public static IEnumerable<T> GetChilds<T>(DependencyObject SourceElement) where T : DependencyObject
        {
            Queue<DependencyObject> CacheQueue = new Queue<DependencyObject>();
            CacheQueue.Enqueue(SourceElement);
            DependencyObject Current;
            int Count;
            while (CacheQueue.Count > 0)
            {
                Current = CacheQueue.Dequeue();
                if (Current is T Result)
                    yield return Result;
                Count = VisualTreeHelper.GetChildrenCount(Current);
                while (Count-- > 0)
                    CacheQueue.Enqueue(VisualTreeHelper.GetChild(Current, Count));
            }
            yield break;
        }

        public static bool GetParent<T>(DependencyObject SourceElement, out T? Result) where T : DependencyObject
        {
            DependencyObject current = SourceElement;
            Result = null;
            while (current != null)
            {
                if (current is T Parent)
                {
                    Result = Parent;
                    return true;
                }
                try { current = VisualTreeHelper.GetParent(current); }
                catch { return false; }
            }
            return false;
        }

        public static IEnumerable<DependencyObject> GetChildsByTypeName(DependencyObject SourceElement, string TarTypeName)
        {
            Queue<DependencyObject> CacheQueue = new Queue<DependencyObject>();
            CacheQueue.Enqueue(SourceElement);
            DependencyObject Current;
            int Count;
            while (CacheQueue.Count > 0)
            {
                Current = CacheQueue.Dequeue();
                if (Current.GetType().Name.Equals(TarTypeName, StringComparison.OrdinalIgnoreCase))
                    yield return Current;
                Count = VisualTreeHelper.GetChildrenCount(Current);
                while (Count-- > 0)
                    CacheQueue.Enqueue(VisualTreeHelper.GetChild(Current, Count));
            }
            yield break;
        }

        public static bool ObservableCollection_DropMove<TControl, TData>(object sender, MouseEventArgs e, DragDropEffects dragDropEffects) where TControl : ItemsControl
        {
            if (sender is FrameworkElement && e.LeftButton == MouseButtonState.Pressed)
            {
                if (e.OriginalSource is not FrameworkElement currentFE || currentFE is null)
                    return false;
                while (currentFE.DataContext is not TData)
                {
                    if (VisualTreeHelper.GetParent(currentFE) is not FrameworkElement fe || fe == null)
                        return false;
                    currentFE = fe;
                }
                if (GetParent(currentFE, out TControl? ListControl))
                {
                    DragDrop.DoDragDrop(currentFE, new DataObject(new DragDropData(typeof(TData), typeof(TControl), $"{ListControl!.Name}", ListControl, currentFE.DataContext)), dragDropEffects);
                    return true;
                }
            }
            return false;
        }
        public static void ObservableCollection_Drop<PickItemType, TData>(object sender, DragEventArgs e, ObservableCollection<TData> TargetList, DragDropEffects dragDropEffects,
            bool? EnableTransboundary = null, bool DisableDeepCopy = false, bool DisableDuplication = true, bool DisableBoundaryLimitation = false)
            where PickItemType : FrameworkElement where TData : class, IDeepCopy<TData>
        {
            DragDropData? dragDropData = e.Data.GetData(typeof(DragDropData)) as DragDropData;
            if (e.Effects != dragDropEffects || dragDropData == null || dragDropData.Data is not TData Data)
                return;

            FrameworkElement? PickItem = e.OriginalSource as FrameworkElement;
            while (PickItem is not PickItemType && PickItem is not null)
                PickItem = (FrameworkElement)VisualTreeHelper.GetParent(PickItem);

            TData? PickSource = PickItem?.DataContext as TData;

            if (!EnableTransboundary.HasValue)
            {
                if (dragDropEffects == DragDropEffects.Move)
                    EnableTransboundary = false;
                else EnableTransboundary = true;
            }

            if (dragDropData.DataSource == sender)
            {
                //拖拽到空白区，直接最后一个
                if (PickItem is null)
                {
                    TargetList.Move(TargetList.IndexOf(Data), TargetList.Count - 1);
                    return;
                }
                if (PickSource is null)
                    return;
                TargetList.Move(TargetList.IndexOf(Data), GetPickIndex(TargetList, e, PickItem, PickSource));
            }
            else
            {
                if (!EnableTransboundary.Value)
                    return;
                if (DisableDeepCopy && DisableDuplication && TargetList.Contains(Data))
                {
                    MainWindow.ShowStatusInfo("已存在");
                    return;
                }
                //拖拽到空白区，直接最后一个
                if (PickItem is null)
                {
                    TargetList.Add(DisableDeepCopy ? Data : Data.DeepCopy());
                    return;
                }
                if (PickSource is null)
                    return;
                TargetList.Insert(GetPickIndex(TargetList, e, PickItem, PickSource, DisableBoundaryLimitation), DisableDeepCopy ? Data : Data.DeepCopy());
            }
        }
        public static int GetPickIndex<T>(ObservableCollection<T> Source, DragEventArgs e, FrameworkElement PickFrameworkElement, T PickData, bool DisableBoundaryLimitation = false)
        {
            double MouseY = e.GetPosition(PickFrameworkElement).Y;
            double CenterY = PickFrameworkElement.ActualHeight / 2;

            int PickIndex = Source.IndexOf(PickData!);

            if (MouseY > CenterY)
                ++PickIndex;
            if (PickIndex < 0)
                PickIndex = 0;
            if (!DisableBoundaryLimitation && PickIndex >= Source.Count)
                PickIndex = Source.Count - 1;
            return PickIndex;
        }

        public static void UIThreadBeginInvoke(Action action)
        {
            if (action == null)
                return;
            if (Application.Current.Dispatcher.Thread == Thread.CurrentThread)
                action();
            else
                Application.Current.Dispatcher.BeginInvoke(action);
        }

        public static string CheckDuplicateNameAndLegal(string FileFullPath)
        {
            if (CheckDuplicateNameAndLegal(ref FileFullPath, true))
                return FileFullPath;
            throw new ArgumentException($"路径参数值[\"{FileFullPath}\"]非法或根目录不存在");
        }
        public static bool CheckDuplicateNameAndLegal(ref string FileFullPath, bool EnableReName = false)
        {
            if (!Directory.Exists(Path.GetPathRoot(FileFullPath)))
                return false;
            if (EnableReName)
            {
                while (File.Exists(FileFullPath))
                    FileFullPath = Path.Combine(Path.GetDirectoryName(FileFullPath)!, $"{Path.GetFileNameWithoutExtension(FileFullPath)}(重命名){Path.GetExtension(FileFullPath)}");
                return true;
            }
            else return !File.Exists(FileFullPath);
        }
        public static bool CheckDirectoryPath(string FullDirectoryPath, out string ErrorInfo, bool EnableCreate = true)
        {
            ErrorInfo = string.Empty;
            if (string.IsNullOrWhiteSpace(FullDirectoryPath))
            {
                ErrorInfo = $"[{nameof(FullDirectoryPath)}]参数是空或空白，无效值！";
                return false;
            }
            if (!Path.IsPathRooted(FullDirectoryPath))
            {
                ErrorInfo = $"路径[{FullDirectoryPath}]参数中无有效根路径，路径格式错误！此处需要完整的本地磁盘路径";
                return false;
            }
            if (!Directory.Exists(Path.GetPathRoot(FullDirectoryPath)))
            {
                ErrorInfo = $"路径[{FullDirectoryPath}]参数中指定的根目录不存在于此计算机，无效值！";
                return false;
            }
            if (!Directory.Exists(FullDirectoryPath))
            {
                if (EnableCreate)
                {
                    Directory.CreateDirectory(FullDirectoryPath);
                    return true;
                }
                ErrorInfo = $"路径[{FullDirectoryPath}]不存在于此计算机！";
                return false;
            }
            return true;
        }
        public static void CleanDirectory(string path, bool EnableToRecycle = false)
        {
            if (EnableToRecycle)
                Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory(path, Microsoft.VisualBasic.FileIO.UIOption.AllDialogs, Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
            else
                Directory.Delete(path, true);
            Directory.CreateDirectory(path);
        }
        public static string FFmpegMergeArguments(string InPath, string OutPath) => $"-hide_banner -f concat -safe 0 -i \"{InPath}\" -c copy \"{OutPath}\" -y";
        public static bool DirectoryEquals(string? DirectoryA, string? DirectoryB) => !string.IsNullOrEmpty(DirectoryA) && !string.IsNullOrEmpty(DirectoryB) && DirectoryA.TrimEnd('/', '\\').Equals(DirectoryB.TrimEnd('/', '\\'));

        public static bool IsDuplicates<T>(this IEnumerable<T> Source, IEqualityComparer<T> comparer = null!)
        {
            Set<T> set = new Set<T>(comparer);
            foreach (T Item in Source)
            {
                if (!set.Add(Item))
                    return true;
            }
            return false;
        }
        public static IEnumerable<T> WhereKeyIntersection<T, T2, TKeySelector>(this IEnumerable<T> Source, IEnumerable<T2> FilterCollection, Func<T, TKeySelector> SourceKeySelector, Func<T2, TKeySelector> FilterKeySelector)
        {
            if (Source == null || Source.Count() < 1)
                yield break;

            if (FilterCollection == null || FilterCollection.Count() < 1)
            {
                foreach (T Item in Source)
                    yield return Item;
                yield break;
            }

            FrozenSet<TKeySelector> set = FilterCollection.Select(p => FilterKeySelector(p)).ToFrozenSet();
            foreach (T Item in Source)
            {
                if (set.Contains(SourceKeySelector(Item)))
                    yield return Item;
            }
        }
        public static void ClearEmptyDirectory(string Path)
        {
            if (!Directory.Exists(Path))
                return;
            Directory.GetFiles(Path, "*", SearchOption.AllDirectories).ToArray().ForEach(p => File.Delete(p));
            Directory.GetDirectories(Path, "*", SearchOption.AllDirectories).ToArray().ForEach((p) => Directory.Delete(p));
        }

        public static double GetValid(this double value) { if (!double.IsFinite(value)) return 0; return value; }

        public static string GenerateRandomString(int length)
        {
            if (length <= 0) throw new ArgumentException("长度必须大于0");

            using (RandomNumberGenerator? rng = RandomNumberGenerator.Create())
            {
                var result = new char[length];
                byte[] randomBytes = new byte[length * 4];
                rng.GetBytes(randomBytes);

                for (int i = 0; i < length; i++)
                {
                    uint randomValue = BitConverter.ToUInt32(randomBytes, i * 4);
                    result[i] = RandomCharacters[randomValue % (uint)RandomCharacters.Length];
                }
                return new string(result);
            }
        }

        public static void WaitUntil(Func<bool> Condition, int SpinCount = 5, int SpinWaitCycles = 100, int SleepInterval = 50)
        {
            int m_CurrentSpin = 0;
            while (!Condition())
            {
                if (m_CurrentSpin++ < SpinCount)
                    Thread.SpinWait(SpinWaitCycles);
                else
                    Thread.Sleep(SleepInterval);
            }
        }

        public static void WaitUntil(ref bool Condition, int SpinCount = 5, int SpinWaitCycles = 100, int SleepInterval = 50)
        {
            int m_CurrentSpin = 0;
            while (!Condition)
            {
                if (m_CurrentSpin++ < SpinCount)
                    Thread.SpinWait(SpinWaitCycles);
                else
                    Thread.Sleep(SleepInterval);
            }
        }
        public unsafe static float Average(this float[] values)
        {
            if (values == null || values.Length == 0)
                return 0f;

            float sum = 0;
            int i = 0;
            int vectorSize = Vector<float>.Count;

            // SIMD处理部分
            if (values.Length >= vectorSize)
            {
                Vector<float> sumVector = Vector<float>.Zero;
                for (; i <= values.Length - vectorSize; i += vectorSize)
                {
                    var v = new Vector<float>(values, i);
                    sumVector += v;
                }

                // 将SIMD结果累加到标量
                for (int j = 0; j < vectorSize; j++)
                    sum += sumVector[j];
            }

            // 处理剩余元素
            for (; i < values.Length; i++)
                sum += values[i];

            return sum / values.Length;
        }
        private static int GetPixelRowNumber(int byteArrayIndex, int imageWidth)
        {
            int pixelIndex = byteArrayIndex / 4;  // 计算像素位置
            return pixelIndex / imageWidth;       // 计算行号
        }

        private unsafe static (int FirstNonZeroIndex, int LastNonZeroIndex) FindNonZeroBoundsSIMD(Span<byte> data, byte value)
        {
            int firstIndex = -1;
            int lastIndex = -1;
            int i = 0;
            Vector<byte> filledVector = System.Numerics.Vector.Create(value);

            // 正向扫描查找第一个非零值
            fixed (byte* dataPtr = data)
            {
                byte* end = dataPtr + data.Length;
                byte* current = dataPtr;
                // SIMD处理对齐部分
                for (; current < end; i += Vector<byte>.Count, current += Vector<byte>.Count)
                {
                    Vector<byte> vector = System.Numerics.Vector.Load(current);//new Vector<byte>(data.Slice(i, Vector<byte>.Count));
                    if (System.Numerics.Vector.GreaterThanAny(vector, filledVector))
                    //if (!System.Numerics.Vector.EqualsAll(vector, Vector<byte>.Zero))
                    {
                        byte* e = current + Vector<byte>.Count;
                        int ind = i;
                        for (; current < e; current++, ind++)
                        {
                            if (*current > value)
                            {
                                firstIndex = ind;
                                break;
                            }
                        }
                        break;
                    }
                }
            }
            // 处理剩余部分（如果还没找到）
            if (firstIndex == -1)
            {
                for (; i < data.Length; i++)
                {
                    if (data[i] > value)
                    {
                        firstIndex = i;
                        break;
                    }
                }
            }

            // 反向扫描查找最后一个非零值
            i = data.Length - 1;
            fixed (byte* dataPtr = data)
            {
                byte* current = dataPtr + data.Length - Vector<byte>.Count;
                // SIMD处理对齐部分
                for (; current >= dataPtr; i -= Vector<byte>.Count, current -= Vector<byte>.Count)
                {
                    Vector<byte> vector = System.Numerics.Vector.Load(current);
                    if (System.Numerics.Vector.GreaterThanAny(vector, filledVector))
                    {
                        // 找到非零块，检查具体位置
                        byte* e = current + Vector<byte>.Count;
                        for (int j = Vector<byte>.Count - 1; j >= 0; j--)
                        {
                            if (current[j] > value)
                            {
                                lastIndex = i - (Vector<byte>.Count - 1 - j);
                                break;
                            }
                        }
                        break;
                    }
                }
            }
            // 处理剩余部分（如果还没找到）
            if (lastIndex == -1)
            {
                for (; i >= 0; i--)
                {
                    if (data[i] > value)
                    {
                        lastIndex = i;
                        break;
                    }
                }
            }

            return (firstIndex, lastIndex);
        }

        public static void FindImageBoundsSIMD(Span<byte> pixels, byte AlphaTransparentIimit, int imageWidth, out int TopBounds, out int BottomBounds)
        {
            (int FirstNonZeroIndex, int LastNonZeroIndex) Indexs = FindNonZeroBoundsSIMD(pixels, AlphaTransparentIimit);
            TopBounds = GetPixelRowNumber(Indexs.FirstNonZeroIndex, imageWidth);
            BottomBounds = GetPixelRowNumber(Indexs.LastNonZeroIndex, imageWidth);
        }
        public static void FindImageBounds(Span<byte> data, int Imageheight, int stride, byte AlphaTransparentIimit, out int TopBounds, out int BottomBounds)
        {
            int CurrentIndex, RowEnd;
            bool RowValue;
            TopBounds = BottomBounds = 0;
            for (int y = 0; y < Imageheight; y++)
            {
                CurrentIndex = y * stride;       //推进到下一行
                RowEnd = CurrentIndex + stride;  //计算下一行结尾位置
                CurrentIndex += 3;               //推进到下一行首个像素的Alpha索引的位置
                RowValue = false;
                for (; CurrentIndex < RowEnd; CurrentIndex += 4) //每次一个像素，跳过RGB直接到下一个像素的Alpha索引的位置
                {
                    if (data[CurrentIndex] <= AlphaTransparentIimit)
                        continue;
                    RowValue = true;  //当前行非空
                    if (TopBounds == 0)
                    {
                        TopBounds = y;
                        break;        //跳出当前行
                    }
                    break;            //跳出当前行
                }
                if (TopBounds != 0 && !RowValue)
                {
                    BottomBounds = y - 1;
                    return;
                }
            }
        }

        public static async Task DelayedExecution(Action action, TimeSpan DelayedTime = default, bool UseMainThread = false)
        {
            ArgumentNullException.ThrowIfNull(action, nameof(action));
            if (DelayedTime == TimeSpan.Zero) DelayedTime = OneSecond;
            await Task.Delay(DelayedTime).ConfigureAwait(false);
            if (UseMainThread)
                await Application.Current.Dispatcher.BeginInvoke(action);
            else action.Invoke();
        }
        public static async Task<TResult> DelayedExecution<TResult>(Func<TResult> func, TimeSpan DelayedTime = default, Func<Task<bool>>? condition = null, bool UseMainThread = false)
        {
            //System.Diagnostics.Contracts.Contract.
            ArgumentNullException.ThrowIfNull(func, nameof(func));
            if (DelayedTime == TimeSpan.Zero) DelayedTime = OneSecond;
            await Task.Delay(DelayedTime).ConfigureAwait(false);
            if (condition is not null && !await condition())
            {
                LogManage.WriteLineNewInfo($"延迟调用条件失败");
                return default!;
            }
            LogManage.WriteLineNewInfo($"延迟执行");
            if (UseMainThread)
                return await Application.Current.Dispatcher.InvokeAsync(func);
            return func.Invoke();
        }

        public static bool GetJsonFromLog(string log, out JsonNode[] result)
        {
            result = null!;
            MatchCollection Mats = GetJsonRegex.Matches(log);
            if (!Mats.Any())
                return false;
            JsonNode? jn;
            List<JsonNode> jsonNodes = new List<JsonNode>();
            foreach (Match m in Mats)
            {
                jn = JsonNode.Parse(m.Value);
                if (jn is null)
                    continue;
                jsonNodes.Add(jn);
            }
            if (jsonNodes.Any())
            {
                result = jsonNodes.ToArray();
                return true;
            }
            return false;
        }
    }
}
